package com.paziy.jan.calc.literals;

import com.paziy.jan.calc.literals.functions.Sin;
import com.paziy.jan.calc.literals.numbers.Num;
import com.paziy.jan.calc.literals.operations.Div;
import com.paziy.jan.calc.literals.operations.Mul;
import com.paziy.jan.calc.literals.operations.Sub;
import com.paziy.jan.calc.literals.operations.Sum;
import com.paziy.jan.calc.literals.operations.exeptions.WrongCountOfOperands;

import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.regex.Pattern;

public abstract class Computable extends Literal {

    //todo delete this block
    static private Map<Pattern,Class<? extends Computable>> computables;
    static {
        computables = new HashMap<Pattern, Class<? extends Computable>>();

        computables.put(Pattern.compile("\\+"), Sum.class);
        computables.put(Pattern.compile("\\-"), Sub.class);
        computables.put(Pattern.compile("\\*"), Mul.class);
        computables.put(Pattern.compile("[/:]"), Div.class);

        computables.put(Pattern.compile("[Ss][Ii][Nn]"), Sin.class);
    }

    public static class PriorityComparator implements Comparator<Computable> {

        @Override
        public int compare(Computable o1, Computable o2) {
            return o1.getPriority() - o2.getPriority();
        }
    }

    protected final byte COUNT_OF_OPERANDS;

    protected final byte PRIORITY;

    public Computable(String pattern, byte countOfOperands, byte priority) {
        super(pattern);
        COUNT_OF_OPERANDS = countOfOperands;
        PRIORITY = priority;
    }

    public byte getCountOperands() {
        return COUNT_OF_OPERANDS;
    }

    public byte getPriority() {
        return PRIORITY;
    }

    protected void checkCountOfOperands(LinkedList<Num> stack) throws WrongCountOfOperands {
        if(stack.size() != COUNT_OF_OPERANDS) throw new WrongCountOfOperands();
    }

    protected boolean checks(LinkedList<Num> stack) {
        return true;
    }

    public final Num apply(LinkedList<Num> stack) throws Exception {

        checkCountOfOperands(stack);

        if (checks(stack)) {
            return calculating(stack);
        }
        else {
            return null;
        }
    }

    protected abstract Num calculating(LinkedList<Num> stack) throws WrongCountOfOperands;

    //todo delete this method
    public static Map<Pattern,Class<? extends Computable>> getComputables() {
        return computables;
    }
}
